<?php
/**
* This BaseClassSingleton class holds all the Atoms, and provides the tools to access them.
*<p>The AtomCache is stored in the <i>site_data/data/catalog</i> file and is automatically unserialized
*upon engine initialization, or created new if missing. Then, during engine shutdown any modified AtomData or
*the catalog itself (if modified) are serialized to disk.</p>
*<p>The AtomCache is mostry a static class. All its members are static escept for the destructor and the 
*Atoms array. This is done because no other data needs to be serialzed, but it needs to be kept in memory to
*speed up the engine and make it scale up as the number of Atoms grow.</p>
*<p>Mostof the methods in this class are for internal use only.</p>
*@package Atoms
*@author Willy Gardiol <willy@gardiol.org>
*@version 1.0
*@todo MAke sure that only here during shutdown the CoreAuth::Authorize is checked, nowhere else is needed.
*@todo Study impact of scaling to lots of Atoms and check on memory usage
*/
class AtomCache extends BaseClassSingleton
{
	static private $modified = false;
	static private $filename = "site_data/data/catalog";
	static private $modified_data = array();
	/**
	* Force  all the Atoms cache to be serialized on shutdown.
	*@access private
	*/
	static public function SetModified()
	{
		AtomCache::$modified = true;
	}
	/**
	* Load or create the atoms cache.
	*@access private
	*/	
	static public function Startup()
	{
		if ( !file_exists( "site_data/data" ) )
			mkdir( "site_data/data" );
		if ( file_exists( AtomCache::$filename ) )
			unserialize( file_get_contents( AtomCache::$filename ) );
		else
		{
			new AtomCache();
			AtomCache::SetModified();
		}
	}
	/**
	* Delete the atom cache and force call of destructor.
	*@access private
	*/	
	static public function Shutdown()
	{
		BaseClassSingleton::DeleteInstance("AtomCache");
	}
	/**
	* Return an atom given its id.
	*@param string $atom_id Atom id to retrieve
	*@return Atom or false if atom id is invalid
	*/
	static public function GetAtom( $atom_id )
	{
		$me = BaseClassSingleton::GetInstance("AtomCache");
		# Do not proceed if $id is not a valid atom id
		if ( !isset( $me->atoms[$atom_id] ) )
			return false;
		return $me->atoms[$atom_id];
	}
	/**
	* Return an atom data given its atom id.
	*@param string $atom_id Atom id to retrieve data from
	*@return AtomData or false if atom id is invalid
	*/
	static public function GetAtomData( $atom_id )
	{	# Get datum
		if ( ($atom = AtomCache::GetAtom($atom_id)) !== false )
			return $atom->LoadData();
		return false;
	}
	/**
	* Force a specific AtomData to be serialized on shutdown.
	*@access private
	*@param string $atom_id Atom id
	*/
	static public function DataModified( $atom_id )
	{
		AtomCache::$modified_data[$atom_id] = true;
	}
	/**
	* Add a new atom to the Atom Cache.
	*@return bool
	*@param Atom $atom new Atom to register
	*@access private
	*/
	static public function RegisterAtom( $atom )
	{
		if ( get_class( $atom ) == "Atom" )
		{
			$me = BaseClassSingleton::GetInstance("AtomCache");
			$atom_id = $atom->GetId();
			if ( !isset( $me->atoms[$atom_id] ) )
			{
				$me->atoms[$atom_id] = $atom;
				AtomCache::SetModified();
			}
			return true;
		}
		return false;
	}
	/**
	* Get a list of atoms matching tags.
	*@param array $tags An array containing all the tags to filter atoms for (empty=any tags)
	*@return array In the form atom_id => atom class instance
	*/
	static public function GetAtoms( $tags = array() )
	{
		$me = BaseClassSingleton::GetInstance("AtomCache");
		$ret = array();
		foreach ($me->atoms as $id => $atom )
		{
			$cloud = $atom->GetTagCloud();
			$tag_match = true;
			foreach ( $tags as $t )
				if ( !in_array( $t, $cloud ) )
				{
					$tag_match = false;
					break;
				}
			if ( $tag_match )
				$ret[$id] = $atom;
		}
		return $ret;
	}
	/**
	* Delete an atom (its actually moved to trashcan).
	*@param string $atom_id The atom id of the atom to delete
	*@param bool $force if set to true, will delete an atmo with leaves and parents.
	*@return bool
	*/
	static public function DeleteAtom( $atom_id, $force = false )
	{	# Get myself
		$me = BaseClassSingleton::GetInstance("AtomCache");
		# Do not proceed if $id is not a valid datum id
		if ( !isset( $me->atoms[$atom_id] ) )
			return false;

		if ( !file_exists("site_data/data/trash" ) )
			mkdir( "site_data/data/trash" );

		if ( $me->atoms[$atom_id]->MoveToTrash($force) )
		{
			unset( $me->atoms[$atom_id] );
			unset( AtomCache::$modified_data[$atom_id] );
			AtomCache::SetModified();
			return true;
		}
		return false;
	}

	private $atoms = array();
	/**
	* Check is user is authorized, then save all modified AtomData, then save the AtomCache if modified.
	*/
	public function __destruct()
	{	# Unset all AutoStore instanced that have been loaded, to force serialization is required
		foreach ( AtomCache::$modified_data as $atom_id => $data )
			$this->atoms[ $atom_id ]->StoreData();
		AtomCache::$modified_data = array();

		# Save myself if needed
		if ( AtomCache::$modified )
			file_put_contents( AtomCache::$filename, serialize( $this ) );
	}
}
AtomCache::Startup();
?>